package com.bluexmicro.bluetooth.scan

import android.Manifest
import android.annotation.SuppressLint
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.bluetooth.le.BluetoothLeScanner
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanResult
import android.bluetooth.le.ScanSettings
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.core.app.ActivityCompat
import com.bluexmicro.bluetooth.model.ScanFailure
import com.bluexmicro.bluetooth.model.ScanMode
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeUnit

@SuppressLint("MissingPermission")
class ScannerImpl : IScanner {

    private val scanning = MutableStateFlow(false)
    override val isScanning: StateFlow<Boolean> = scanning

    private val _bluetoothError = MutableStateFlow(false)
    override val bluetoothError = _bluetoothError.asStateFlow()

    private var scanner: BluetoothLeScanner? = null
    private var appContext: Context? = null
    private var resultMap = ConcurrentHashMap<String, ScanResult>()//线程安全、多线程环境中操作map
    private val handler = Handler(Looper.getMainLooper())


    override fun scanForPeripherals(
        context: Context,
        scanMode: ScanMode /*= ScanSettings.SCAN_MODE_LOW_LATENCY*/
    ) {
        handler.removeCallbacks(runnable)
        appContext = context.applicationContext
        scanner = null
        //清空旧数据
        resultMap.clear()
        val bluetoothAdapter = fetchAvailableBluetoothAdapter()
        if (bluetoothAdapter != null) {
            val scanSettings = ScanSettings.Builder()
                .setScanMode(scanMode.value)
                .setReportDelay(0) //如果reportDelay>0的话，第一次report太慢了
                .build()
            scanner = try {
                bluetoothAdapter.bluetoothLeScanner?.apply {
                    startScan(null, scanSettings, scanCallback)
                }
            } catch (e: Exception) {
                Log.e("TAG", "scanForPeripherals: $e")
                e.printStackTrace()
                null
            }
        }
        _bluetoothError.update { false }
        scanning.update { scanner != null }
    }


    override fun stopScan() {
        if (fetchAvailableBluetoothAdapter() != null) {
            scanner?.stopScan(scanCallback)
        }
        scanning.update { false }
    }

    private val runnable = Runnable { stopScan() }

    /**
     * 蓝牙不允许30s内重复开关超过5次
     */
    fun delayStop() {
        handler.removeCallbacks(runnable)
        handler.postDelayed(runnable, 6000L)
    }


    override fun fetchScanResults(
        expiredTimeSeconds: Long /*= 10L //大于0即过滤*/
    ): List<ScanResult> {
        if (expiredTimeSeconds > 0) {
            removeOldScanResults(resultMap, expiredTimeSeconds)
        }
        return resultMap.values.toList()
    }

    override fun clearCache() {
        resultMap.clear()
    }

    private val scanCallback = object : ScanCallback() {
        override fun onScanResult(callbackType: Int, result: ScanResult) {
            resultMap[result.device.address] = result
        }

        override fun onBatchScanResults(results: MutableList<ScanResult>) {
            for (result in results) {
                resultMap[result.device.address] = result
            }

        }

        override fun onScanFailed(errorCode: Int) {
            Log.e("Bluetooth", "onScanFailed: ${ScanFailure.fromValue(errorCode).name}")
            if (errorCode == SCAN_FAILED_APPLICATION_REGISTRATION_FAILED) {
                _bluetoothError.value = true
            }
            scanning.update { false }
        }
    }

    private fun removeOldScanResults(
        map: ConcurrentHashMap<String, ScanResult>,
        olderThan: Long,
        timeUnit: TimeUnit = TimeUnit.SECONDS
    ) {
        val thresholdNanos = System.nanoTime() - timeUnit.toNanos(olderThan)
        map.entries.removeAll { (_, value) ->
            value.timestampNanos < thresholdNanos
        }
    }

    private fun fetchAvailableBluetoothAdapter(): BluetoothAdapter? {
        val ctx = appContext ?: return null

        val permissionState = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            ActivityCompat.checkSelfPermission(
                ctx,
                Manifest.permission.BLUETOOTH_SCAN
            ) == PackageManager.PERMISSION_GRANTED
        } else {
            true
        }

        if (permissionState.not()) return null

        val manager = ctx.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager?
        val bluetoothAdapter = manager?.adapter ?: return null
        return if (bluetoothAdapter.isEnabled) bluetoothAdapter else null
    }

}